home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
daymisckit_proj
/
daymisckit-1
/
DAYString.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
29KB
|
1,080 lines
//
// DAYString.m -- a generic class to simplify manipulation of (char *)'s
// Written by Don Yacktman (c) 1993 by Don Yacktman.
// Version 1.2 All rights reserved.
//
// This is a free object! Contact the author for the latest version.
// Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
// e-mail: Don_Yacktman@byu.edu
//
// You may use and copy this class freely as long as you
// comply with the following terms:
// (1) Do not remove the author's name or any of the
// copyright notices from this file.
// (2) If you redistribute an application which uses this
// object, you must either include the source code for
// this object with the application or state in your
// application's documentation that you (a) use this
// object and (b) where to obtain the source code for
// the object.
// (3) In no way shall the author or his employer(s) be held
// responsible for any damages caused by what this object
// does or does not do.
// (4) You have no warranty whatsoever that this object is
// good for any purpose at all. If you find it useful
// for something, consider yourself lucky and leave it at that.
//
//
// And now for an implementation note... (philosophy of this class)
// The object is to make a bulletproof class. That means that NULL
// pointers and values that are out of range are ignored, or that
// nil is returned, or that the object tries to do what would be the
// intelligent thing to do in such boundary cases. Obviously, all
// this error checking costs you something: speed. On the other hand,
// your code shouldn't end up with anywhere near as many bus errors
// and other silliness! (This is not a panacea; don't rely on the
// DAYString to get everything right for you; if a boudary condition
// might exist, do be sure to check return values as they can signal
// problems for you.) There is no substitue for good programming,
// but hopefully this class will help you survive silly mistakes.
// Most of the methods send to other methods to do the real work;
// it would run faster to implement each method separately, since
// some methods would then not need to do certain checks, and so on.
// On the other hand, it is a whole lot easier to maintain this code
// since there are a handful of core methods that do all the work.
// Someday I may attempt to make a class DAYFastString that will
// be optimized for speed... but only if there is either (1) a *whole*
// lot of demand for it, or (2) someone pays me to write it. :-)
//
// Returns: I try to follow NeXT conventions. Return self, nil, or
// whatever makes sense in the context. Hopefully your idea of
// what makes sense will coincide with mine. Read the docs...
//
// For this release (1.2), I have gone over every single method and
// tried to make sure it behaved sanely on all possible boundary
// cases, especially NULL pointers. If there is any way to
// break this class, or cause a bus error in here, etc. or any
// other bugs I WANT to know about them... be sure to send bug
// reports to me at Don_Yacktman@byu.edu so I can fix them. I
// want this class to be bulletproof. Note that there are still
// a few possibilities to get memory leaks here. I want to fix
// those, if at all possible, so remind me of the ones you find.
// I got rid of the most obvious and common ones.
//
// One memory leak possibility, and this happens in the test app a
// little bit: you call a method that returns a new object, but
// then do something like this:
// printf("%s", [[aString right:5] stringValue]);
// A new instance is created, but never freed. I need a way around this,
// like a method -stringValueAndFree that returns a const char * and
// then free itself. The trick is that the returned char * is still
// around. The most elegant solution I can think of is to run the
// pointer through NXUniqueString() first, and then return it, after
// freeing the object. This way the pointer is never lost, and you
// won't really leak memory, but the string space sure will get cluttered.
// If you have an idea on how to fix this problem, let me know. I like
// the convenience of the above printf, but do not like the memory leak.
// You could always to this:
// temp = [aString right:5];
// printf("%s", [temp stringValue]);
// [temp free];
// But that's a pain in the *ss...
// Well, send me your suggestions.
//
#import <daymisckit/DAYString.h>
#import <strings.h>
@implementation DAYString
- init
{
[super init];
[self setStringOrderTable:NXDefaultStringOrderTable()];
if (buffer) free(buffer);
buffer = NULL;
length = 0;
_length = 0;
return self;
}
- initString:(const char *)aString
{
[self init];
return [self setStringValue:aString];
}
- setStringOrderTable:(NXStringOrderTable *)table
{
if (table) orderTable = table;
else orderTable = NXDefaultStringOrderTable(); // just in case...
return self;
}
- (NXStringOrderTable *)stringOrderTable
{
return orderTable;
}
- allocateBuffer:(int)size
{
return [self allocateBuffer:size fromZone:[self zone]];
}
- allocateBuffer:(int)size fromZone:(NXZone *)zone
{
if (size <= _length) return self;
[self freeString];
if (!size) return self;
buffer = (char *)NXZoneMalloc(zone, size);
buffer[0] = 0;
_length = size;
return self;
}
- setStringValue:(const char *)aString
{
return [self setStringValue:aString fromZone:[self zone]];
}
- setStringValue:(const char *)aString fromZone:(NXZone *)zone
{
if (!aString) return self; // use -freeString to set to "NULL"
// Note that I could have used NXCopyStringBufferFromZone() here
// instead. This works just as well, but it may be slower...
// I haven't checked that out, though. It might be worth doing.
[self allocateBuffer:strlen(aString)+1 fromZone:zone];
strcpy(buffer, aString);
length = strlen(buffer);
return self;
}
- takeStringValue:sender
{ // if no string value, return nil; the user is expecting a changed string
if (![sender respondsTo:@selector(stringValue)]) return nil;
return [self setStringValue:[sender stringValue] fromZone:[self zone]];
}
- takeStringValue:sender fromZone:(NXZone *)zone
{
if (![sender respondsTo:@selector(stringValue)]) return nil;
return [self setStringValue:[sender stringValue] fromZone:zone];
}
- (const char *)stringValue
{
return buffer;
}
- read:(NXTypedStream *)stream
{
int tempLength; char *tBuf;
[super read:stream];
NXReadType(stream, "i", &tempLength);
[self allocateBuffer:tempLength fromZone:[self zone]];
NXReadType(stream, "*", &tBuf);
if (tBuf) strcpy(buffer, tBuf);
if (!tBuf && buffer) buffer[0] = '\0'; // NULL string?
return self;
}
- write:(NXTypedStream *)stream
{
[super write:stream];
NXWriteType(stream, "i", &_length);
NXWriteType(stream, "*", &buffer);
return self;
}
- copyFromZone:(NXZone *)zone
{
DAYString *myCopy = [super copyFromZone:zone];
// force child to have it's own copy of the string buffer
[myCopy _unhookBuffer];
[myCopy allocateBuffer:_length fromZone:zone];
[myCopy setStringValue:buffer fromZone:zone];
return myCopy;
}
- _unhookBuffer
{ // used by the copy method so that we don't free the buffer from orig.
buffer = NULL; _length = 0;
return self;
}
- freeString
{
if(buffer) free(buffer);
buffer = NULL;
length = 0;
_length = 0;
return self;
}
- free
{
[self freeString];
return [super free];
}
- cat:(const char *)aString
{
return [self cat:aString n:strlen(aString) fromZone:[self zone]];
}
- cat:(const char *)aString n:(int)n
{
return [self cat:aString n:n fromZone:[self zone]];
}
- cat:(const char *)aString fromZone:(NXZone *)zone
{
return [self cat:aString n:strlen(aString) fromZone:zone];
}
- cat:(const char *)aString n:(int)n fromZone:(NXZone *)zone
{
char *newBuffer; int newSize;
if (!(aString || buffer)) return nil;
if (!buffer) return [self setStringValue:aString fromZone:zone];
if (!aString) return self;
if (n > strlen(aString)) n = strlen(aString);
newSize = length + n + 1;
if (newSize > _length) {
newBuffer = (char *)NXZoneMalloc(zone, newSize);
_length = newSize;
newBuffer[0] = '\0';
strcat(newBuffer, buffer);
strncat(newBuffer, aString, n);
free(buffer);
buffer = newBuffer;
} else strncat(buffer, aString, n);
length = strlen(buffer);
return self;
}
- concatenate:sender
{ // note return self here; assume that there's nothing to add...
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self cat:[sender stringValue]
n:strlen([sender stringValue])
fromZone:[self zone]];
}
- concatenate:sender n:(int)n
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self cat:[sender stringValue] n:n fromZone:[self zone]];
}
- concatenate:sender fromZone:(NXZone *)zone
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self cat:[sender stringValue]
n:strlen([sender stringValue]) fromZone:zone];
}
- concatenate:sender n:(int)n fromZone:(NXZone *)zone
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self cat:[sender stringValue] n:n fromZone:zone];
}
- (const char *)strstr:(const char *)subString
{
if (!(buffer||subString)) return NULL;
return strstr(buffer, subString);
}
- subStringRight:subString
{
const char *sub, *sub2;
if (!buffer) return nil;
if ([subString respondsTo:@selector(stringValue)])
sub = [subString stringValue];
else return nil; // error if can't get string value
if (!sub) return nil;
sub2 = strstr(buffer,sub);
if (!sub2) return nil;
return [[DAYString allocFromZone:[self zone]]
initString:sub2];
}
- subStringLeft:subString
{
/* Old implementation: ugly and has potential memory leak
const char *sub;
char *tempString, *temp2;
id retVal = [DAYString alloc];
if ([subString respondsTo:@selector(stringValue)])
sub = [subString stringValue];
else return nil; // error if can't get string value
tempString = NXCopyStringBufferFromZone(buffer, [self zone]);
temp2 = strstr(tempString, sub);
if (temp2) {
temp2[0] = '\0'; // terminate it early
[retVal initString:tempString];
} else { // substring not found
return [self copy];
}
free(tempString);
return retVal;
*/
const char *sub;
char *sub2;
int spot;
if ([subString respondsTo:@selector(stringValue)])
sub = [subString stringValue];
else return nil; // error if can't get string value
if (!sub) return nil;
if (!(sub2 = strstr(buffer, sub))) return nil;
spot = (int)sub2 - (int)buffer - 1;
if (spot < 0) return nil;
return [self midFrom:0 to:spot];
}
- (int)length
{
return length;
}
- (BOOL)isEqual:anObject
{
if (anObject == self) return YES;
// doesn't have to be a DAYString object to be equal...
if ([anObject respondsTo:@selector(stringValue)]) {
if (!NXOrderStrings(buffer, [anObject stringValue],
YES, -1, orderTable)) return YES;
}
return NO;
}
- (int)compareTo:sender
{
return [self compareTo:sender n:(-1) caseSensitive:YES];
}
- (int)compareTo:sender n:(int)n
{
return [self compareTo:sender n:n caseSensitive:YES];
}
- (int)cmp:(const char *)aString
{
if (!aString && !buffer) return 0; // both NULL, so "equal"
if (!aString || !buffer) return -1; // only one is NULL, so not equal
return strcmp(buffer, aString);
}
- (int)cmp:(const char *)aString n:(int)n
{
if (!aString && !buffer) return 0; // both NULL, so "equal"
if (!aString || !buffer) return -1; // only one is NULL, so not equal
// we don't check n; use strncmp's behavior here.
return strncmp(buffer, aString, n);
}
- (int)compareTo:sender caseSensitive:(BOOL)sense
{
return [self compareTo:sender n:(-1) caseSensitive:sense];
}
- (int)compareTo:sender n:(int)n caseSensitive:(BOOL)sense
{
if (![sender respondsTo:@selector(stringValue)]) return 1; // !=
if (!buffer && ![sender stringValue]) return 0; // both are NULL
if (!(buffer && [sender stringValue])) return 1; // one is NULL
return NXOrderStrings(buffer, [sender stringValue], sense, n, orderTable);
}
- (int)casecmp:(const char *)aString
{
if (!aString && !buffer) return 0; // both NULL, so "equal"
if (!aString || !buffer) return -1; // only one is NULL, so not equal
return strcasecmp(buffer, aString);
}
- (int)casecmp:(const char *)aString n:(int)n
{
if (!aString && !buffer) return 0; // both NULL, so "equal"
if (!aString || !buffer) return -1; // only one is NULL, so not equal
// we don't check n; use strncasecmp's behavior here.
return strncasecmp(buffer, aString, n);
}
- left:(int)count
{
return [self left:count fromZone:[self zone]];
}
- right:(int)count
{
return [self right:count fromZone:[self zone]];
}
- midFrom:(int)start to:(int)end
{
return [self midFrom:start to:end fromZone:[self zone]];
}
- midFrom:(int)start length:(int)len
{
return [self midFrom:start length:len fromZone:[self zone]];
}
- left:(int)count fromZone:(NXZone *)zone
{
char smash = buffer[count];
id newString;
if (!buffer) return nil;
if (count >= length) return [self copyFromZone:zone];
newString = [[DAYString allocFromZone:zone] init];
buffer[count] = '\0';
[newString setStringValue:buffer fromZone:zone];
buffer[count] = smash;
return newString;
}
- right:(int)count fromZone:(NXZone *)zone
{
id newString;
if (!buffer) return nil;
if (count >= length) return [self copyFromZone:zone];
newString = [[DAYString allocFromZone:zone] init];
[newString setStringValue:&buffer[length - count] fromZone:zone];
return newString;
}
- midFrom:(int)start to:(int)end fromZone:(NXZone *)zone
{
char smash = buffer[end+1];
id newString;
if (!buffer) return nil;
if ((end < 0) || (start >= length)) return nil;
if (end >= length) end = length-1;
if (start < 0) start = 0;
newString = [[DAYString allocFromZone:zone] init];
buffer[end+1] = '\0'; // inclusive; end-1 is not. (well, end isn't either, anymore -- Carl)
[newString setStringValue:&buffer[start] fromZone:zone];
buffer[end+1] = smash;
return newString;
}
- midFrom:(int)start length:(int)len fromZone:(NXZone *)zone
{
return [self midFrom:start to:(start + len - 1) fromZone:zone];
/* faster to have our own code here, but rather than maintain this,
we use the cover above. I'm keeping the code around in case I
decide to do DAYFastString.
register int spot = start + len;
char smash = buffer[spot];
id newString;
if (!buffer) return nil;
if ((end < 0) || (start >= length)) return nil;
if (start < 0) start = 0;
if (start+len-1 >= length) len = length-start;
newString = [[DAYString allocFromZone:zone] init];
buffer[spot] = '\0';
[newString setStringValue:&buffer[start] fromZone:zone];
buffer[spot] = smash;
return newString;
*/
}
- encrypt:salt
{ // encrypt as a UNIX password using the DAYString "salt" as the salt...
// see crypt(3) for more info
// The cast prevents a warning: -stringValue returns a const char *.
// Assuming crypt() doesn't change the salt, the cast is OK.
char *strv;
if ([salt respondsTo:@selector(stringValue)])
strv = (char *)[salt stringValue];
else return nil;
if (!(buffer && strv)) return nil;
return [[DAYString alloc] initString:crypt(buffer, strv)];
}
// NXTransport protocol implementation:
- encodeUsing:(id <NXEncoding>)portal
{
[portal encodeData:&_length ofType:"i"];
[portal encodeData:&length ofType:"i"];
[portal encodeData:&buffer ofType:"*"];
return self;
}
- decodeUsing:(id <NXDecoding>)portal
{
int newLen;
[self freeString];
[portal decodeData:&newLen ofType:"i"];
[self allocateBuffer:newLen];
[portal decodeData:&length ofType:"i"];
[portal decodeData:&buffer ofType:"*"];
return self;
}
- encodeRemotelyFor:(NXConnection *)connection
freeAfterEncoding:(BOOL *)flagp isBycopy:(BOOL)isByCopy
{
if (isByCopy) {
*flagp = NO; // object will copy.
return self; //encode object (copy it)
}
*flagp = NO; // object will copy.
// super will encode the proxy otherwise
return [super encodeRemotelyFor:connection
freeAfterEncoding:flagp isBycopy:isByCopy];
}
// Interface Builder support
- (const char *)getInspectorClassName { return "DAYStringInspector"; }
- (NXImage *)getIBImage { return [NXImage findImageNamed:"DAYStringObj"]; }
// some other new methods V1.2 -- don
- extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense fromZone:(NXZone *)zone
{
int left = ((n == DAY_FIRST) ? 0 :
( n == DAY_LAST ?
([self rspotOf:c occurenceNum:1 caseSensitive:sense] + 1) :
([self spotOf:c occurenceNum:(n-1) caseSensitive:sense] + 1)));
int right = ((n == DAY_FIRST) ?
([self spotOf:c occurenceNum:1 caseSensitive:sense] - 1) :
( n == DAY_LAST ? length :
[self spotOf:c occurenceNum:n caseSensitive:sense] - 1));
return [self midFrom:left to:right fromZone:zone];
}
- extractPart:(int)n useAsDelimiter:(char)c caseSensitive:(BOOL)sense
{
return [self extractPart:n useAsDelimiter:c
caseSensitive:sense fromZone:[self zone]];
}
- extractPart:(int)n useAsDelimiter:(char)c fromZone:(NXZone *)zone
{
return [self extractPart:n useAsDelimiter:c
caseSensitive:YES fromZone:zone];
}
- extractPart:(int)n useAsDelimiter:(char)c
{
return [self extractPart:n useAsDelimiter:c\
caseSensitive:YES fromZone:[self zone]];
}
- fileNameFromZone:(NXZone *)zone
{
return [self extractPart:DAY_LAST useAsDelimiter:'/'
caseSensitive:YES fromZone:[self zone]];
}
- fileName
{
return [self fileNameFromZone:[self zone]];
}
- pathNameFromZone:(NXZone *)zone
{
return [self left:[self rspotOf:'/'] fromZone:zone];
}
- pathName
{
return [self pathNameFromZone:[self zone]];
}
//Carl's additions....
+ newWithString:(const char *)aString
// I just got tired of typing [[[DAYString alloc] init] setStringValue:xxx]
{
id newString = [[DAYString alloc] init]; // should I use [[self alloc]... ?
if ([newString setStringValue:aString]) return newString;
[newString free];
return nil;
}
- wordNum :(int) num
// returns a new String containing the numth word in buffer
// if numth word does not exist, returns nil.
{
int i = 0;
int currword = 1;
int spot = 0;
int spot2 = 0;
if (!buffer) return nil;
while ((currword <= num) && (i <= length)) {
while ((NXIsSpace(buffer[i])) && (i <= length)) i++;
spot2 = i;
while ((!NXIsSpace(buffer[i])) && (i <= length)) i++;
spot = i;
currword++;
}
if (spot == spot2) return nil;
return [self midFrom :spot2 length:spot-spot2];
}
- (int)numWords
{ //counts the number of words in buffer.
int i=0;
int currword = 0;
if (!buffer) return 0;
while (i <= length) {
while ((NXIsSpace(buffer[i])) && (i <= length)) i++;
while ((!NXIsSpace(buffer[i])) && (i <= length)) i++;
currword++;
}
if (NXIsSpace(buffer[length-1])) currword--;
return currword;
}
- trimLeadSpaces
{ // removes any leading spaces from buffer
int i = 0;
id tmpStr;
if (!buffer) return self;
while (buffer[i] == ' ') i++;
if (i==0) return self;
tmpStr = [self right:length-i];
[self takeStringValue:tmpStr];
[tmpStr free];
return self;
}
- trimTailSpaces
// removes any trailing spaces from buffer
{
int i = length;
id tmpStr;
if (!buffer) return self;
while (buffer[i-1] == ' ') i--;
if (i==length) return self;
tmpStr = [self left:i];
[self takeStringValue:tmpStr];
[tmpStr free];
return self;
}
- trimSpaces
// takes off leading and trailing spaces of the buffer
{
return [[self trimLeadSpaces] trimTailSpaces];
}
- reverse
// reverses the characters in the buffer. If it's a palindrome, you won't
// notice much of a difference :-)
{
char tmp[length+1];
int j=0;
int i;
if (length <= 1) return self;
for (i=length-1;i>=0;i--)
{
tmp[j] = buffer[i];
j++;
}
tmp[length] = 0;
if (length != 0) [self setStringValue:tmp];
return self;
}
- toUpper
// converts any lowercase characters in buffer to uppercase
{
int i;
for (i=0;i<length;i++)
{
if (NXIsLower(buffer[i])) buffer[i] = NXToUpper(buffer[i]);
}
return self;
}
- toLower
// converts any uppercase chars in buffer to lowercase
{
int i;
for (i=0;i<length;i++)
{
if (NXIsUpper(buffer[i])) buffer[i] = NXToLower(buffer[i]);
}
return self;
}
- insert:(const char *)aString at:(int)index
// inserts given string into buffer starting at index.
// (the first character is position #0)
{
id temp1;
id temp2;
if ((aString == NULL) || (strlen(aString)<=0)) return self;
if (index < 0) index = 0;
if (index >= length) return [self cat:aString];
temp1 = [self left:index];
temp2 = [self right:length-index];
[[temp1 cat:aString] concatenate:temp2];
[self takeStringValue:temp1];
[temp1 free];
[temp2 free];
return self;
}
- insertString:(id)sender at:(int)index
// cover for insert:at: for a String object
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self insert:[sender stringValue] at:index];
}
- insertChar:(char)aChar at:(int)index
{
id tempStr;
id retval;
if (aChar == 0) return self; // or should this return nil? DAY: leave it
tempStr = [[[[DAYString alloc] init] allocateBuffer:2] addChar:aChar];
retval = [self insert:[tempStr stringValue] at:index];
[tempStr free];
return retval;
}
- (char) charAt:(int)index
{
if ((index < 0) || (index >length-1)) return 0;
return (char)buffer[index];
}
- removeFrom:(int)index length:(int)len
{ // to avoid memory leaks, this should NEVER return nil!
id temp1,temp2;
if (!buffer) return self; // everything's already gone
if (len <= 0) return self; // noting to remove
// DAY: should I presume to fix index<0 like so? or just index = 0?
// or just return self?
if (index < 0) { length += index; index = 0; if (len <= 0) return self; }
if (index > length - 1) return self; // nothing out there
temp1 = [self left:index];
temp2 = [self midFrom:index+len to:length];
[temp1 concatenate:temp2];
[self takeStringValue:temp1];
[temp1 free];
[temp2 free];
return self;
}
- removeFrom:(int)start to:(int)end
{
return [self removeFrom:start length:end-start+1];
}
- replaceFrom:(int)start length:(int)len with:(const char *)aString
{
return [[self removeFrom:start length:len] insert:aString at:start];
}
- replaceFrom:(int)start to:(int)end with:(const char *)aString
{
return [[self removeFrom:start to:end] insert:aString at:start];
}
- replaceFrom:(int)start length:(int)len withString:(id)sender
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self replaceFrom:start length:len with:[sender stringValue]];
}
- replaceFrom:(int)start to:(int)end withString:(id)sender
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self replaceFrom:start to:end with:[sender stringValue]];
}
- replace:(const char *)subString with:(const char *)newString
{
const char *spot;
if (spot = [self strstr:subString])
[self replaceFrom:(int)(spot-buffer) length:strlen(subString) with:newString];
return self;
}
- replace:(const char *)subString withString:(id)sender
{
if (![sender respondsTo:@selector(stringValue)]) return self;
return [self replace:subString with:[sender stringValue]];
}
- (int)spotOf:(char)aChar
{
return [self spotOf:aChar occurenceNum:1 caseSensitive:YES];
}
- (int)spotOf:(char)aChar caseSensitive:(BOOL)sense
{
return [self spotOf:aChar occurenceNum:1 caseSensitive:sense];
}
- (int)spotOf:(char)aChar occurenceNum:(int)n
{
return [self spotOf:aChar occurenceNum:n caseSensitive:YES];
}
- (int)rspotOf:(char)aChar
{
return [self rspotOf:aChar occurenceNum:1 caseSensitive:YES];
}
- (int)rspotOf:(char)aChar caseSensitive:(BOOL)sense
{
return [self rspotOf:aChar occurenceNum:1 caseSensitive:sense];
}
- (int)rspotOf:(char)aChar occurenceNum:(int)n
{
return [self rspotOf:aChar occurenceNum:n caseSensitive:YES];
}
- (int)spotOf:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
{
int currnum = 0;
int count = 0;
if (n<1) return -1;
while ((currnum < n) && (count<length)) {
if (!sense) {
if (NXToUpper(buffer[count]) == NXToUpper(aChar)) currnum++;
}
else {
if (buffer[count] == aChar) currnum++;
}
count++;
}
if (currnum != n) return -1;
return (count-1);
}
- (int)rspotOf:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
{
int currnum = 0;
int count = length-1;
if (n<1) return -1;
while ((currnum < n) && (count >= 0)) {
if (!sense) {
if (NXToUpper(buffer[count]) == NXToUpper(aChar)) currnum++;
}
else {
if (buffer[count] == aChar) currnum++;
}
count--;
}
if (currnum != n) return -1;
return (count+1);
}
- addChar:(char)aChar
{
if (aChar) [self cat:&aChar n:1];
return self;
}
- squashSpaces
{
int count = 0;
id tempStr;
if (!buffer) return self;
[self trimSpaces];
tempStr = [[[DAYString alloc] init] allocateBuffer:length];
while (count<length) {
while (buffer[count]!=' ') {
[tempStr addChar:buffer[count]];
count++;
}
if ((count<length) && (buffer[count] == ' ')) {
[tempStr addChar:buffer[count]];
count++;
}
if ((count<length) && (buffer[count]==' ') &&
((buffer[count-2] == ':') || (buffer[count-2] =='.'))) {
[tempStr addChar:buffer[count]];
count++;
}
while (buffer[count]==' ') count++;
}
[self takeStringValue:tempStr];
[tempStr free];
return self;
}
- (const char *)rindex:(char)aChar
{
return [self rindex:aChar occurenceNum:1 caseSensitive:YES];
}
- (const char *)rindex:(char)aChar occurenceNum:(int)n
{
return [self rindex:aChar occurenceNum:n caseSensitive:YES];
}
- (const char *)rindex:(char)aChar caseSensitive:(BOOL)sense
{
return [self rindex:aChar occurenceNum:1 caseSensitive:sense];
}
- (const char *)rindex:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
{
int num;
num = [self rspotOf:aChar occurenceNum:n caseSensitive:sense];
if (num == -1) return NULL;
return buffer+num;
}
- (const char *)index:(char)aChar
{
return [self index:aChar occurenceNum:1 caseSensitive:YES];
}
- (const char *)index:(char)aChar occurenceNum:(int)n
{
return [self index:aChar occurenceNum:n caseSensitive:YES];
}
- (const char *)index:(char)aChar caseSensitive:(BOOL)sense
{
return [self index:aChar occurenceNum:1 caseSensitive:sense];
}
- (const char *)index:(char)aChar occurenceNum:(int)n caseSensitive:(BOOL)sense
{
int num;
num = [self spotOf:aChar occurenceNum:n caseSensitive:sense];
if (num==-1) return NULL;
return buffer+num;
}
- (int)endcmp:(const char *)aString
{
return [self endcmp:aString n:-1];
}
- (int)endcmp:(const char *)aString n:(int)n
{
id tempStr;
int retval;
if (!aString) return -1;
tempStr = [DAYString newWithString:aString];
retval = [self endCompareTo:tempStr n:n caseSensitive:YES];
[tempStr free];
return retval;
}
- (int)endcasecmp:(const char *)aString
{
return [self endcasecmp:aString n:-1];
}
- (int)endcasecmp:(const char *)aString n:(int)n
{
id tempStr;
int retval;
if (!aString) return -1;
tempStr = [DAYString newWithString:aString];
retval = [self endCompareTo:tempStr n:n caseSensitive:NO];
[tempStr free];
return retval;
}
- (int)endCompareTo:(id)sender
{
return [self endCompareTo:sender n:-1 caseSensitive:YES];
}
- (int)endCompareTo:(id)sender caseSensitive:(BOOL)sense
{
return [self endCompareTo:sender n:-1 caseSensitive:sense];
}
- (int)endCompareTo:(id)sender n:(int)n
{
return [self endCompareTo:sender n:n caseSensitive:YES];
}
- (int)endCompareTo:(id)sender n:(int)n caseSensitive:(BOOL)sense
{ // DAY: fixed it: sender doesn't have to be a DAYString anymore.
id tempStr, temp2, temp3,
smaller = ((length >= [sender length]) ? sender : self);
int retval, smallLen;
if (!([sender respondsTo:@selector(stringValue)])) return -1;
if (![sender stringValue] || !buffer) return -1;
if ([sender respondsTo:@selector(length)]) smallLen = [smaller length];
else smallLen = strlen([smaller stringValue]);
if ((n == -1) || (n > smallLen)) n = smallLen;
tempStr = [self right:n];
if ([sender respondsTo:@selector(right:)]) temp2 = [sender right:n];
else {
temp3 = [DAYString newWithString:[sender stringValue]];
temp2 = [temp3 right:n];
[temp3 free];
}
retval = [tempStr compareTo:temp2 caseSensitive:sense];
[tempStr free];
[temp2 free];
return retval;
}
- replaceCharAt:(int)index with:(char)aChar
{ // no change if index is out of range.
// Other option is to limit index to be in range. which is better?
if ((index < 0) || (index >= length)) return self;
if (aChar == 0) return self; // should this return nil? DAY: leave it.
buffer[index] = aChar;
return self;
}
- replaceFrom:(int)start length:(int)len withChar:(char)aChar
{
id retval,tempStr;
if (aChar == 0) return nil;
tempStr = [[[DAYString alloc] init] addChar:aChar];
retval = [self replaceFrom:start length:len
withString:tempStr];
[tempStr free];
return retval;
}
- replaceFrom:(int)start to:(int)end withChar:(char)aChar
{
return [self replaceFrom:start length:end-start+1 withChar:aChar];
}
@end